Check properly whether the XPM module has already been loaded
authorHavoc Pennington <hp@pobox.com>
Wed, 27 Oct 1999 18:55:00 +0000 (18:55 +0000)
committerHavoc Pennington <hp@src.gnome.org>
Wed, 27 Oct 1999 18:55:00 +0000 (18:55 +0000)
1999-10-27  Havoc Pennington  <hp@pobox.com>

* src/gdk-pixbuf-io.c (gdk_pixbuf_new_from_xpm_data): Check
properly whether the XPM module has already been loaded
(gdk_pixbuf_new_from_file): Check properly if loader module
was already loaded (was checking if load symbol was present
in order to decide whether to re-load; should check module !=
NULL, then load != NULL)
(image_handler_load): Check in present working directory,
makes it easier to debug for now
(file_formats): This array initializer was seriously on crack,
was assigning a function pointer to a GModule*

* src/testpixbuf.c (main): Change type of pixbuf_loader to GdkPixbufLoader*

* src/io-png.c: Progressive loading stuff compiles, untested.

demos/testpixbuf.c
gdk-pixbuf/gdk-pixbuf-io.c
gdk-pixbuf/io-png.c

index 20dc5f79a60c90863beeb61d57f7944009c0cfbf..fc6d0aa397320238137805625c302f4eb73bd9ac 100644 (file)
@@ -409,7 +409,7 @@ main (int argc, char **argv)
        int found_valid = FALSE; 
 
        GdkPixbuf *pixbuf;
-       GtkObject *pixbuf_loader;
+       GdkPixbufLoader *pixbuf_loader;
        FILE *file;
        gint val;
        guchar buf;
index 236fdaeb23fa7354cd2399bd340269734d56f8d8..9c41402cb583dd635d34e49a6e0c5ee434c0e78a 100644 (file)
@@ -136,17 +136,17 @@ pixbuf_check_ppm (guchar *buffer, int size)
 #endif
 
 ModuleType file_formats [] = {
-       { "png",  pixbuf_check_png,  NULL, NULL, NULL, NULL, NULL, NULL },
-       { "jpeg", pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL, NULL },
-       { "tiff", pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL, NULL },
-       { "gif",  pixbuf_check_gif,  NULL, NULL, NULL, NULL, NULL, NULL },
+       { "png",  NULL, pixbuf_check_png,  NULL, NULL, NULL, NULL, NULL },
+       { "jpeg", NULL, pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL },
+       { "tiff", NULL, pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL },
+       { "gif",  NULL, pixbuf_check_gif,  NULL, NULL, NULL, NULL, NULL },
 #define XPM_FILE_FORMAT_INDEX 4
-       { "xpm",  pixbuf_check_xpm,  NULL, NULL, NULL, NULL, NULL, NULL },
+       { "xpm",  NULL, pixbuf_check_xpm,  NULL, NULL, NULL, NULL, NULL },
 #if 0
-       { "bmp",  pixbuf_check_bmp,  NULL, NULL, NULL, NULL, NULL, NULL },
-       { "ppm",  pixbuf_check_ppm,  NULL, NULL, NULL, NULL, NULL, NULL },
+       { "bmp",  NULL, pixbuf_check_bmp,  NULL, NULL, NULL, NULL, NULL },
+       { "ppm",  NULL, pixbuf_check_ppm,  NULL, NULL, NULL, NULL, NULL },
 #endif
-       { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+       { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static void
@@ -161,14 +161,29 @@ image_handler_load (ModuleType *image_module)
 
        module_name = g_strconcat ("pixbuf-", image_module->module_name, NULL);
        path = g_module_build_path (PIXBUF_LIBDIR, module_name);
-       g_free (module_name);
-
+        
        module = g_module_open (path, G_MODULE_BIND_LAZY);
-       g_free (path);
        if (!module) {
-               g_warning ("Unable to load module: %s", path);
-               return;
-       }
+                /* Debug feature, check in present working directory */
+                g_free(path);
+                path = g_module_build_path("", module_name);
+                module = g_module_open(path, G_MODULE_BIND_LAZY);
+
+                if (!module) {
+                        g_warning ("Unable to load module: %s: %s", path, g_module_error());
+                        g_free (module_name);
+                        g_free(path);
+                        return;
+                } else {
+                  printf("loaded module `%s'\n", module_name);
+                }
+                g_free(path);
+       } else {
+                printf("loaded module `%s'\n", path);
+                g_free (path);
+        }
+
+        g_free (module_name);
 
        image_module->module = module;
 
@@ -224,10 +239,10 @@ gdk_pixbuf_new_from_file (const char *filename)
 
        image_module = gdk_pixbuf_get_module (buffer, size);
        if (image_module){
-               if (!image_module->load)
+               if (image_module->module == NULL)
                        image_handler_load (image_module);
 
-               if (!image_module->load) {
+               if (image_module->load == NULL) {
                        fclose (f);
                        return NULL;
                }
@@ -254,13 +269,16 @@ gdk_pixbuf_new_from_xpm_data (const gchar **data)
         GdkPixbuf *(* load_xpm_data) (const gchar **data);
         GdkPixbuf *pixbuf;
 
-        if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) {
+        if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) {
                 image_handler_load(&file_formats[XPM_FILE_FORMAT_INDEX]);
         }
 
-        if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) {
+        if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) {
                 g_warning("Can't find gdk-pixbuf module for parsing inline XPM data");
                 return NULL;
+        } else if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) {
+                g_warning("gdk-pixbuf XPM module lacks XPM data capability");
+                return NULL;
         } else {
                 load_xpm_data = file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data;
         }
index a60adfbdc428c47df963a6f41189a2bbd8a93090..fed2e7e806bdfeb9c600e2900f2a2c4f5e4b9b26 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdio.h>
 #include <png.h>
 #include "gdk-pixbuf.h"
+#include "gdk-pixbuf-io.h"
 
 \f
 
@@ -135,8 +136,299 @@ image_load (FILE *f)
                                                 free_buffer, NULL);
 }
 
+/* These avoid the setjmp()/longjmp() crap in libpng */
+static void png_error_callback  (png_structp png_read_ptr,
+                                 png_const_charp error_msg);
+
+static void png_warning_callback(png_structp png_read_ptr,
+                                 png_const_charp warning_msg);
+
+/* Called at the start of the progressive load */
+static void png_info_callback   (png_structp png_read_ptr,
+                                 png_infop   png_info_ptr);
+
+/* Called for each row; note that you will get duplicate row numbers
+   for interlaced PNGs */
+static void png_row_callback   (png_structp png_read_ptr,
+                                png_bytep   new_row,
+                                png_uint_32 row_num,
+                                int pass_num);
+
+/* Called after reading the entire image */
+static void png_end_callback   (png_structp png_read_ptr,
+                                png_infop   png_info_ptr);
+
+typedef struct _LoadContext LoadContext;
+
+struct _LoadContext {
+        png_structp png_read_ptr;
+        png_infop   png_info_ptr;
+
+        ModulePreparedNotifyFunc notify_func;
+        gpointer notify_user_data;
+
+        GdkPixbuf* pixbuf;
+        
+        guint fatal_error_occurred : 1;
+
+};
+
+gpointer
+image_begin_load (ModulePreparedNotifyFunc func, gpointer user_data)
+{
+        LoadContext* lc;
+        
+        lc = g_new0(LoadContext, 1);
+        
+        lc->fatal_error_occurred = FALSE;
+
+        lc->notify_func = func;
+        lc->notify_user_data = user_data;
+
+        /* Create the main PNG context struct */
+
+        lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+                                                  lc, /* error/warning callback data */
+                                                  png_error_callback,
+                                                  png_warning_callback);
+
+        if (lc->png_read_ptr == NULL) {
+                g_free(lc);
+                return NULL;
+        }
+
+        /* Create the two auxiliary context structs */
+
+        lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
+
+        if (lc->png_info_ptr == NULL) {
+                png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
+                g_free(lc);
+                return NULL;
+        }
+
+        png_set_progressive_read_fn(lc->png_read_ptr,
+                                    lc, /* callback data */
+                                    png_info_callback,
+                                    png_row_callback,
+                                    png_end_callback);
+        
+
+        return lc;
+}
+
+void
+image_stop_load (gpointer context)
+{
+        LoadContext* lc = context;
+
+        g_return_if_fail(lc != NULL);
+
+        gdk_pixbuf_unref(lc->pixbuf);
+        
+        png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
+        g_free(lc);
+}
+
 gboolean
-image_load_by_data (void *data, size_t count)
+image_load_increment(gpointer context, guchar *buf, guint size)
+{
+        LoadContext* lc = context;
+
+        g_return_val_if_fail(lc != NULL, FALSE);
+
+        /* Invokes our callbacks as needed */
+        png_process_data(lc->png_read_ptr, lc->png_info_ptr, buf, size);
+
+        if (lc->fatal_error_occurred)
+                return FALSE;
+        else
+                return TRUE;
+}
+
+/* Called at the start of the progressive load, once we have image info */
+static void
+png_info_callback   (png_structp png_read_ptr,
+                     png_infop   png_info_ptr)
+{
+        LoadContext* lc;
+        png_uint_32 width, height;
+        int bit_depth, color_type, filter_type,
+          compression_type, interlace_type, channels;
+        gboolean have_alpha = FALSE;
+        
+        lc = png_get_progressive_ptr(png_read_ptr);
+
+        if (lc->fatal_error_occurred)
+                return;
+        
+        /* Get the image info */
+
+        png_get_IHDR (lc->png_read_ptr, lc->png_info_ptr,
+                      &width, &height,
+                      &bit_depth,
+                      &color_type,
+                      &interlace_type,
+                      &compression_type,
+                      &filter_type);
+
+        /* set_expand() basically needs to be called unless
+           we are already in RGB/RGBA mode
+        */
+        if (color_type == PNG_COLOR_TYPE_PALETTE &&
+            bit_depth <= 8) {
+
+                /* Convert indexed images to RGB */
+                png_set_expand (lc->png_read_ptr);
+
+        } else if (color_type == PNG_COLOR_TYPE_GRAY &&
+                   bit_depth < 8) {
+
+                /* Convert grayscale to RGB */
+                png_set_expand (lc->png_read_ptr);
+
+        } else if (png_get_valid (lc->png_read_ptr, 
+                                  lc->png_info_ptr, PNG_INFO_tRNS)) {
+
+                /* If we have transparency header, convert it to alpha
+                   channel */
+                png_set_expand(lc->png_read_ptr);
+                
+        } else if (bit_depth < 8) {
+
+                /* If we have < 8 scale it up to 8 */
+                png_set_expand(lc->png_read_ptr);
+
+
+                /* Conceivably, png_set_packing() is a better idea;
+                 * God only knows how libpng works
+                 */
+        }
+
+        /* If we are 16-bit, convert to 8-bit */
+        if (bit_depth == 16) {
+                png_set_strip_16(lc->png_read_ptr);
+        }
+
+        /* If gray scale, convert to RGB */
+        if (color_type == PNG_COLOR_TYPE_GRAY ||
+            color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+                png_set_gray_to_rgb(lc->png_read_ptr);
+        }
+        
+        /* If we have alpha, set a flag */
+        if (color_type & PNG_COLOR_MASK_ALPHA)
+          have_alpha = TRUE;
+
+        /* Update the info the reflect our transformations */
+        png_read_update_info(lc->png_read_ptr, lc->png_info_ptr);
+        
+        png_get_IHDR (lc->png_read_ptr, lc->png_info_ptr,
+                      &width, &height,
+                      &bit_depth,
+                      &color_type,
+                      &interlace_type,
+                      &compression_type,
+                      &filter_type);
+
+#ifndef G_DISABLE_CHECKS
+        /* Check that the new info is what we want */
+        
+        if (bit_depth != 8) {
+                g_warning("Bits per channel of transformed PNG is %d, not 8.", bit_depth);
+                lc->fatal_error_occurred = TRUE;
+                return;
+        }
+
+        if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
+                color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
+                g_warning("Transformed PNG not RGB or RGBA.");
+                lc->fatal_error_occurred = TRUE;
+                return;
+        }
+
+        channels = png_get_channels(lc->png_read_ptr, lc->png_info_ptr);
+        if ( ! (channels == 3 || channels == 4) ) {
+                g_warning("Transformed PNG has %d channels, must be 3 or 4.", channels);
+                lc->fatal_error_occurred = TRUE;
+                return;
+        }
+#endif
+
+        lc->pixbuf = gdk_pixbuf_new(have_alpha, width, height);
+
+        if (lc->pixbuf == NULL) {
+                /* Failed to allocate memory */
+                lc->fatal_error_occurred = TRUE;
+                return;
+        }
+        
+        /* Notify the client that we are ready to go */
+
+        if (lc->notify_func)
+                (* lc->notify_func) (lc->pixbuf, lc->notify_user_data);
+        
+        return;
+}
+
+/* Called for each row; note that you will get duplicate row numbers
+   for interlaced PNGs */
+static void
+png_row_callback   (png_structp png_read_ptr,
+                    png_bytep   new_row,
+                    png_uint_32 row_num,
+                    int pass_num)
+{
+        LoadContext* lc;
+        guchar* old_row = NULL;
+        
+        lc = png_get_progressive_ptr(png_read_ptr);
+
+        if (lc->fatal_error_occurred)
+                return;
+                
+        old_row = lc->pixbuf->art_pixbuf->pixels + (row_num * lc->pixbuf->art_pixbuf->rowstride);
+        
+        png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
+}
+
+/* Called after reading the entire image */
+static void
+png_end_callback   (png_structp png_read_ptr,
+                    png_infop   png_info_ptr)
 {
-       return TRUE;
+        LoadContext* lc;
+
+        lc = png_get_progressive_ptr(png_read_ptr);
+
+        if (lc->fatal_error_occurred)
+                return;
+
+        /* Doesn't do anything for now */
 }
+
+
+static void
+png_error_callback(png_structp png_read_ptr,
+                   png_const_charp error_msg)
+{
+        LoadContext* lc;
+        
+        lc = png_get_error_ptr(png_read_ptr);
+        
+        lc->fatal_error_occurred = TRUE;
+        
+        fprintf(stderr, "Fatal error loading PNG: %s\n", error_msg);
+}
+
+static void
+png_warning_callback(png_structp png_read_ptr,
+                     png_const_charp warning_msg)
+{
+        LoadContext* lc;
+        
+        lc = png_get_error_ptr(png_read_ptr);
+        
+        fprintf(stderr, "Warning loading PNG: %s\n", warning_msg);
+}
+